#include "dev_sdcard.h"

static SD_HandleTypeDef        SDCARD_Handler;                    /* SD卡句柄 */
static HAL_SD_CardInfoTypeDef  SDCardInfo = {0};                  /* SD卡信息结构体 */
static DMA_HandleTypeDef       SDTxDMAHandler, SDRxDMAHandler;    /* SD卡DMA发送和接收句柄 */
static volatile uint8_t        sd_wr_status = DEV_SD_OP_NO, sd_rd_status = DEV_SD_OP_NO; /* SD卡读写状态 */ 
__ALIGN_BEGIN static uint8_t   sd_one_block_buff[BLOCKSIZE] __ALIGN_END;

RET_STATUS dev_sdcard_init(void)
{
    /* 初始化时的时钟不能大于400KHZ */
    SDCARD_Handler.Instance                 = SDIO;
    SDCARD_Handler.Init.ClockEdge           = SDIO_CLOCK_EDGE_RISING;                  /* 上升沿 */
    SDCARD_Handler.Init.ClockBypass         = SDIO_CLOCK_BYPASS_DISABLE;               /* 不使用bypass模式，直接用HCLK进行分频得到SDIO_CK */
    SDCARD_Handler.Init.ClockPowerSave      = SDIO_CLOCK_POWER_SAVE_DISABLE;           /* 空闲时不关闭时钟电源 */
    SDCARD_Handler.Init.BusWide             = SDIO_BUS_WIDE_1B;                        /* 1位数据线 */
    SDCARD_Handler.Init.HardwareFlowControl = SDIO_HARDWARE_FLOW_CONTROL_DISABLE;      /* 关闭硬件流控 */
    SDCARD_Handler.Init.ClockDiv            = SDIO_TRANSFER_CLK_DIV;                   /* SD传输时钟频率最大25MHZ */

    if (HAL_SD_Init(&SDCARD_Handler) != HAL_OK)
    {
        return RET_ERROR;
    }

    /* 使能宽总线模式 (4 bits mode selected) */
    if (HAL_SD_ConfigWideBusOperation(&SDCARD_Handler, SDIO_BUS_WIDE_4B) != HAL_OK)
    {
        return RET_ERROR;
    }

    return RET_OK;
}

void dev_sdcard_show_info(void)
{   
    /* 获得SD卡信息 */
    if (HAL_SD_GetCardInfo(&SDCARD_Handler, &SDCardInfo) != HAL_OK)
    {
        return;
    }

    /* SD卡类型 */
    switch(SDCardInfo.CardType)
    {
        case CARD_SDSC:
            rt_kprintf("SD Standard Capacity <2G\r\n");
            break;
        
        case CARD_SDHC_SDXC:
            rt_kprintf("SD High Capacity <32G, SD Extended Capacity <2T\r\n");
            break;
        
        case CARD_SECURED:
            rt_kprintf("SD Secured\r\n");
            break;
    }

    rt_kprintf("SD Card RCA: %d\r\n", SDCardInfo.RelCardAdd);                       //卡相对地址
    rt_kprintf("SD Card BlockNumber: %d\r\n", SDCardInfo.LogBlockNbr);              //显示块数量
    rt_kprintf("SD Card BlockSize: %d\r\n", SDCardInfo.LogBlockSize);                //显示块大小
    
    return;
}

/* 获得SD卡状态 */
uint8_t dev_sdcard_get_status(void)
{
    uint8_t ret_status;

    if (HAL_SD_GetCardState(&SDCARD_Handler) == HAL_SD_CARD_TRANSFER)
    {
        ret_status = DEV_SD_TRANSFER_OK;
    }
    else
    {
        ret_status = DEV_SD_TRANSFER_BUSY;
    }

    return ret_status;
}

/* 检测SD卡处于无数据传输状态 */
static uint8_t s_dev_sdcard_wait_ready(uint32_t timeout)
{
    uint32_t start_tick = HAL_GetTick();
    uint32_t current_tick;

    /* break, when timeout or SDIO ready */
    while(1)
    {
        current_tick = HAL_GetTick();

        if ((current_tick - start_tick) > timeout)
        {
            break;  /* timeout */
        }

        if (dev_sdcard_get_status() == DEV_SD_TRANSFER_OK)
        {
            return DEV_SD_WAIT_READY;
        }
    }

    return DEV_SD_WAIT_TIMEOUT;
}

static void s_dev_sdcard_set_write_status(uint8_t status)
{
    sd_wr_status = status;

    return;
}

static uint8_t s_dev_sdcard_get_write_status(void)
{
    return sd_wr_status;
}

static void s_dev_sdcard_set_read_status(uint8_t status)
{
    sd_rd_status = status;

    return;
}

static uint8_t s_dev_sdcard_get_read_status(void)
{
    return sd_rd_status;
}

/**
  * @brief  Erases the specified memory area of the given SD card. 
  * @param  start_addr: Start byte address
  * @param  end_addr: End byte address
  * @retval SD status
  */
RET_STATUS dev_sdcard_erase(uint32_t start_addr, uint32_t end_addr)
{
    RET_STATUS ret_status = RET_OK;

    if (HAL_SD_Erase(&SDCARD_Handler, start_addr, end_addr) != HAL_OK)
    {
        ret_status = RET_ERROR;
    }

    return ret_status;
}

/**
  * @brief  Writes block(s) to a specified address in an SD card, in polling mode. 
  * @param  pdata: Pointer to the buffer that will contain the data to transmit
  * @param  write_addr: Address from where data is to be written
  * @param  blocks_nbr: Number of SD blocks to write
  * @param  timeout: Timeout for write operation
  * @retval SD status
  */
RET_STATUS dev_sdcard_write_blocks(uint32_t *pdata, uint32_t write_addr, uint32_t blocks_nbr, uint32_t timeout)
{
    RET_STATUS ret_status = RET_OK;

    if (HAL_SD_WriteBlocks(&SDCARD_Handler, (uint8_t *) pdata, write_addr, blocks_nbr, timeout) != HAL_OK)
    {
        ret_status = RET_ERROR;
    }

    return ret_status;
}

/**
  * @brief  Reads block(s) to a specified address in an SD card, in polling mode. 
  * @param  pdata: Pointer to the buffer that will contain the data to transmit
  * @param  read_addr: Address from where data is to be read
  * @param  blocks_nbr: Number of SD blocks to read
  * @param  timeout: Timeout for read operation
  * @retval SD status
  */
RET_STATUS dev_sdcard_read_blocks(uint32_t *pdata, uint32_t read_addr, uint32_t blocks_nbr, uint32_t timeout)
{
    RET_STATUS ret_status = RET_OK;

    if (HAL_SD_ReadBlocks(&SDCARD_Handler, (uint8_t *) pdata, read_addr, blocks_nbr, timeout) != HAL_OK)
    {
        ret_status = RET_ERROR;
    }

    return ret_status;
}

/**
  * @brief  Writes block(s) to a specified address in an SD card, in polling mode. 
  * @param  pdata: Pointer to the buffer that will contain the data to transmit
  * @param  write_addr: Address from where data is to be written
  * @param  blocks_nbr: Number of SD blocks to write
  * @retval SD status
  */
RET_STATUS dev_sdcard_write_blocks_dma(uint32_t *pdata, uint32_t write_addr, uint32_t blocks_nbr)
{
    RET_STATUS ret_status = RET_OK;

    /* Write block(s) in DMA transfer mode */
    if (HAL_SD_WriteBlocks_DMA(&SDCARD_Handler, (uint8_t *) pdata, write_addr, blocks_nbr) != HAL_OK)
    {
        ret_status = RET_ERROR;
    }

    return ret_status;
}

/**
  * @brief  Reads block(s) to a specified address in an SD card, in polling mode. 
  * @param  pdata: Pointer to the buffer that will contain the data to transmit
  * @param  read_addr: Address from where data is to be read
  * @param  blocks_nbr: Number of SD blocks to read
  * @retval SD status
  */
RET_STATUS dev_sdcard_read_blocks_dma(uint32_t *pdata, uint32_t read_addr, uint32_t blocks_nbr)
{
    RET_STATUS ret_status = RET_OK;

    /* Read block(s) in DMA transfer mode */
    if (HAL_SD_ReadBlocks_DMA(&SDCARD_Handler, (uint8_t *) pdata, read_addr, blocks_nbr) != HAL_OK)
    {
        ret_status = RET_ERROR;
    }

    return ret_status;
}

/**
  * @brief  SD卡DMA读扇区
  * @param  buff-数据缓冲区
  * @param  sector-扇区地址
  * @param  count-扇区数量
  * @retval SD status
  * @note 该函数由CubeMX生成的Fatfs驱动移植而来,带4字节对齐处理
  */
RET_STATUS dev_sdcard_write_disk_dma(const uint8_t *pbuf, uint32_t sector, uint32_t count)
{
    RET_STATUS ret_status = RET_ERROR;   /* 初始化默认失败 */
    uint32_t   old_tick, i;

    s_dev_sdcard_set_write_status(DEV_SD_OP_NO);    /* 标记SD卡操作未完成 */

    /* 检查SD卡状态 */
    if (s_dev_sdcard_wait_ready(DEV_SD_WAIT_TIME) != DEV_SD_WAIT_READY)
    {
        return RET_ERROR;
    }

    /* 4字节对齐 */
    if (((uint32_t) pbuf & 0x03) != 0)
    {
        return RET_ERROR;
    }

    if (dev_sdcard_write_blocks_dma((uint32_t *) pbuf, sector, count) == RET_OK)  /* 多个扇区一起写入 */
    {
        /* Wait that writing process is completed or a timeout occurs */

        old_tick = HAL_GetTick();

        while ((s_dev_sdcard_get_write_status() == DEV_SD_OP_NO) && ((HAL_GetTick() - old_tick) < DEV_SD_WAIT_TIME)){};

        if (s_dev_sdcard_get_write_status() == DEV_SD_OP_NO)    /* 超时未写成功 */
        {
            ret_status = RET_ERROR;
        }
        else                                                    /* 等待SD卡状态恢复空闲 */
        {
            old_tick = HAL_GetTick();

            while ((HAL_GetTick() - old_tick) < DEV_SD_WAIT_TIME)
            {
                if (dev_sdcard_get_status() == DEV_SD_TRANSFER_OK)
                {
                    ret_status = RET_OK;
                    break;
                }
            }
        }
    }
    else                                                                        /* 扇区逐个写入 */
    {
        for (i = 0; i < count; i++)
        {
            s_dev_sdcard_set_write_status(DEV_SD_OP_NO);    /* 标记SD卡操作未完成 */

            memcpy((void *) sd_one_block_buff, (void *) pbuf, BLOCKSIZE);

            if (dev_sdcard_write_blocks_dma((uint32_t *) sd_one_block_buff, sector++, 1) == RET_OK)
            {
                /* wait for a message from the queue or a timeout */

                old_tick = HAL_GetTick();

                while ((s_dev_sdcard_get_write_status() == DEV_SD_OP_NO) && ((HAL_GetTick() - old_tick) < DEV_SD_WAIT_TIME)){};

                if (s_dev_sdcard_get_write_status() == DEV_SD_OP_NO)    /* 超时未写成功 */
                {
                    break;
                }

                pbuf += BLOCKSIZE;  /* pbuf地址偏移 */
            }
            else    /* 写入失败 */
            {
                break;
            }
        }

        /* 判断写入是否完毕 */
        if (i == count)
        {
            ret_status = RET_OK;
        }
    }

    return ret_status;
}

/**
  * @brief  SD卡DMA读扇区
  * @param  buff-数据缓冲区
  * @param  sector-扇区地址
  * @param  count-扇区数量
  * @retval SD status
  * @note 该函数由CubeMX生成的Fatfs驱动移植而来,带4字节对齐处理
  */
RET_STATUS dev_sdcard_read_disk_dma(uint8_t *pbuf, uint32_t sector, uint32_t count)
{
    RET_STATUS ret_status = RET_ERROR;   /* 初始化默认失败 */
    uint32_t   old_tick, i;

    /* 检查SD卡状态 */
    if (s_dev_sdcard_wait_ready(DEV_SD_WAIT_TIME) != DEV_SD_WAIT_READY)
    {
        return RET_ERROR;
    }

    /* 4字节对齐 */
    if (((uint32_t) pbuf & 0x03) != 0)
    {
        return RET_ERROR;
    }

    s_dev_sdcard_set_read_status(DEV_SD_OP_NO);    /* 标记SD卡操作未完成 */

    if (dev_sdcard_read_blocks_dma((uint32_t *) pbuf, sector, count) == RET_OK)  /* 多个扇区一起读出 */
    {
        /* Wait that reading process is completed or a timeout occurs */

        old_tick = HAL_GetTick();

        while ((s_dev_sdcard_get_read_status() == DEV_SD_OP_NO) && ((HAL_GetTick() - old_tick) < DEV_SD_WAIT_TIME)){};

        if (s_dev_sdcard_get_read_status() == DEV_SD_OP_NO)    /* 超时未读成功 */
        {
            ret_status = RET_ERROR;
        }
        else                                                    /* 等待SD卡状态恢复空闲 */
        {
            old_tick = HAL_GetTick();

            while ((HAL_GetTick() - old_tick) < DEV_SD_WAIT_TIME)
            {
                if (dev_sdcard_get_status() == DEV_SD_TRANSFER_OK)
                {
                    ret_status = RET_OK;
                    break;
                }
            }
        }
    }
    else                                                                        /* 扇区逐个读出 */
    {
        for (i = 0; i < count; i++)
        {
            s_dev_sdcard_set_read_status(DEV_SD_OP_NO);    /* 标记SD卡操作未完成 */

            if (dev_sdcard_read_blocks_dma((uint32_t *) sd_one_block_buff, sector++, 1) == RET_OK)
            {
                /* wait for a message from the queue or a timeout */

                old_tick = HAL_GetTick();

                while ((s_dev_sdcard_get_read_status() == DEV_SD_OP_NO) && ((HAL_GetTick() - old_tick) < DEV_SD_WAIT_TIME)){};

                if (s_dev_sdcard_get_read_status() == DEV_SD_OP_NO)    /* 超时未读成功 */
                {
                    break;
                }

                memcpy((void *) pbuf, (void *) sd_one_block_buff, BLOCKSIZE);
                pbuf += BLOCKSIZE;  /* pbuf地址偏移 */
            }
            else    /* 写入失败 */
            {
                break;
            }
        }

        /* 判断读取是否完毕 */
        if (i == count)
        {
            ret_status = RET_OK;
        }
    }

    return ret_status;
}


/*******************************************************************************************************/

/**
* @brief SD MSP Initialization
* This function configures the hardware resources used in this example
* @param hsd: SD handle pointer
* @retval None
*/
void HAL_SD_MspInit(SD_HandleTypeDef* hsd)
{
    GPIO_InitTypeDef  GPIO_InitStruct = {0};
    
    if(hsd->Instance==SDIO)
    {
        /* USER CODE BEGIN SDIO_MspInit 0 */
  
        /* USER CODE END SDIO_MspInit 0 */
        /* Peripheral clock enable */
        __HAL_RCC_SDIO_CLK_ENABLE();    //使能SDIO时钟
        __HAL_RCC_DMA2_CLK_ENABLE();    //使能DMA2时钟 
        __HAL_RCC_GPIOC_CLK_ENABLE();   //使能GPIOC时钟
        __HAL_RCC_GPIOD_CLK_ENABLE();   //使能GPIOD时钟
          
        /**SDIO GPIO Configuration
        PC8     ------> SDIO_D0
        PC9     ------> SDIO_D1
        PC10     ------> SDIO_D2
        PC11     ------> SDIO_D3
        PC12     ------> SDIO_CK
        PD2     ------> SDIO_CMD
        */
          
        //PC8,9,10,11,12
        GPIO_InitStruct.Pin=GPIO_PIN_8|GPIO_PIN_9|GPIO_PIN_10|GPIO_PIN_11|GPIO_PIN_12;
        GPIO_InitStruct.Mode=GPIO_MODE_AF_PP;      //推挽复用
        GPIO_InitStruct.Pull=GPIO_PULLUP;          //上拉
        GPIO_InitStruct.Speed=GPIO_SPEED_HIGH;     //高速
        GPIO_InitStruct.Alternate=GPIO_AF12_SDIO;  //复用为SDIO
        HAL_GPIO_Init(GPIOC, &GPIO_InitStruct);     //初始化
        
        //PD2
        GPIO_InitStruct.Pin=GPIO_PIN_2;            
        HAL_GPIO_Init(GPIOD, &GPIO_InitStruct);     //初始化
  
        /* USER CODE BEGIN SDIO_MspInit 1 */
        
        //配置接收DMA
        SDRxDMAHandler.Instance=DMA2_Stream3;
        SDRxDMAHandler.Init.Channel=DMA_CHANNEL_4;
        SDRxDMAHandler.Init.Direction=DMA_PERIPH_TO_MEMORY;
        SDRxDMAHandler.Init.PeriphInc=DMA_PINC_DISABLE;
        SDRxDMAHandler.Init.MemInc=DMA_MINC_ENABLE;
        SDRxDMAHandler.Init.PeriphDataAlignment=DMA_PDATAALIGN_WORD;
        SDRxDMAHandler.Init.MemDataAlignment=DMA_MDATAALIGN_WORD;
        SDRxDMAHandler.Init.Mode=DMA_PFCTRL;
        SDRxDMAHandler.Init.Priority=DMA_PRIORITY_VERY_HIGH;
        SDRxDMAHandler.Init.FIFOMode=DMA_FIFOMODE_ENABLE;
        SDRxDMAHandler.Init.FIFOThreshold=DMA_FIFO_THRESHOLD_FULL;
        SDRxDMAHandler.Init.MemBurst=DMA_MBURST_INC4;
        SDRxDMAHandler.Init.PeriphBurst=DMA_PBURST_INC4;
        
        if (HAL_DMA_Init(&SDRxDMAHandler) != HAL_OK)    //初始化接收DMA
        {
            Error_Handler();
        }
  
        __HAL_LINKDMA(hsd, hdmarx, SDRxDMAHandler); //将接收DMA和SD卡的发送DMA连接起来       
        
        //配置发送DMA 
        SDTxDMAHandler.Instance=DMA2_Stream6;
        SDTxDMAHandler.Init.Channel=DMA_CHANNEL_4;
        SDTxDMAHandler.Init.Direction=DMA_MEMORY_TO_PERIPH;
        SDTxDMAHandler.Init.PeriphInc=DMA_PINC_DISABLE;
        SDTxDMAHandler.Init.MemInc=DMA_MINC_ENABLE;
        SDTxDMAHandler.Init.PeriphDataAlignment=DMA_PDATAALIGN_WORD;
        SDTxDMAHandler.Init.MemDataAlignment=DMA_MDATAALIGN_WORD;
        SDTxDMAHandler.Init.Mode=DMA_PFCTRL;
        SDTxDMAHandler.Init.Priority=DMA_PRIORITY_VERY_HIGH;
        SDTxDMAHandler.Init.FIFOMode=DMA_FIFOMODE_ENABLE;
        SDTxDMAHandler.Init.FIFOThreshold=DMA_FIFO_THRESHOLD_FULL;
        SDTxDMAHandler.Init.MemBurst=DMA_MBURST_INC4;
        SDTxDMAHandler.Init.PeriphBurst=DMA_PBURST_INC4;
        
        if (HAL_DMA_Init(&SDTxDMAHandler) != HAL_OK)    //初始化发送DMA
        {
            Error_Handler();
        }
        
        __HAL_LINKDMA(hsd, hdmatx, SDTxDMAHandler);//将发送DMA和SD卡的发送DMA连接起来
      
        /* SDIO interrupt Init */
        HAL_NVIC_SetPriority(SDIO_IRQn, 2, 0);
        HAL_NVIC_EnableIRQ(SDIO_IRQn);
      
        /* DMA interrupt init */
        HAL_NVIC_SetPriority(DMA2_Stream3_IRQn, 3, 0);  //接收DMA中断优先级
        HAL_NVIC_EnableIRQ(DMA2_Stream3_IRQn);
        HAL_NVIC_SetPriority(DMA2_Stream6_IRQn, 3, 0);  //发送DMA中断优先级
        HAL_NVIC_EnableIRQ(DMA2_Stream6_IRQn);
        /* USER CODE END SDIO_MspInit 1 */
    }

}

/**
  * @brief This function handles SDIO global interrupt.
  */
void SDIO_IRQHandler(void)
{
    /* USER CODE BEGIN SDIO_IRQn 0 */

    /* USER CODE END SDIO_IRQn 0 */
    HAL_SD_IRQHandler(&SDCARD_Handler);
    /* USER CODE BEGIN SDIO_IRQn 1 */

    /* USER CODE END SDIO_IRQn 1 */
}

/**
  * @brief This function handles DMA2 stream3 global interrupt.
  */
void DMA2_Stream3_IRQHandler(void)
{
    /* USER CODE BEGIN DMA2_Stream3_IRQn 0 */

    /* USER CODE END DMA2_Stream3_IRQn 0 */
    HAL_DMA_IRQHandler(&SDRxDMAHandler);
    /* USER CODE BEGIN DMA2_Stream3_IRQn 1 */

    /* USER CODE END DMA2_Stream3_IRQn 1 */
}

/**
  * @brief This function handles DMA2 stream6 global interrupt.
  */
void DMA2_Stream6_IRQHandler(void)
{
    /* USER CODE BEGIN DMA2_Stream6_IRQn 0 */
    
    /* USER CODE END DMA2_Stream6_IRQn 0 */
    HAL_DMA_IRQHandler(&SDTxDMAHandler);
    /* USER CODE BEGIN DMA2_Stream6_IRQn 1 */
    
    /* USER CODE END DMA2_Stream6_IRQn 1 */
}

/**
  * @brief Tx Transfer completed callback
  * @param hsd: SD handle
  * @retval None
  */
void HAL_SD_TxCpltCallback(SD_HandleTypeDef *hsd)
{
    s_dev_sdcard_set_write_status(DEV_SD_OP_OK);
    return;
}

/**
  * @brief Rx Transfer completed callback
  * @param hsd: SD handle
  * @retval None
  */
void HAL_SD_RxCpltCallback(SD_HandleTypeDef *hsd)
{
    s_dev_sdcard_set_read_status(DEV_SD_OP_OK);
    return;
}
